﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Pavilion.DesignerTool
{
    public class TrackedContainerNode : TrackedNodeBase
    {
        private ContainerNode containerNode;

        private List<NodeBase> originalNodeCollection;
        private List<Link> originalLinkCollection;
        private Dictionary<NodeBase, TrackedNodeBase> trackedNodeCollection;
        private Dictionary<Link, TrackedLink> trackedLinkCollection;
        private IEnumerable<TrackedNodeBase> activeTrackedNodes;
        private IEnumerable<TrackedLink> activeTrackedLinks;

        public TrackedContainerNode(ContainerNode containerNode, NodeScript parentNodeScript, ResourceItem parentResource) : base(containerNode, parentNodeScript, parentResource)
        {
            this.containerNode = containerNode;

            originalNodeCollection = new List<NodeBase>(containerNode.NodeCollection);
            originalLinkCollection = new List<Link>(containerNode.LinkCollection);

            trackedNodeCollection = new Dictionary<NodeBase, TrackedNodeBase>();
            trackedLinkCollection = new Dictionary<Link, TrackedLink>();

            foreach (NodeBase node in containerNode.NodeCollection)
            {
                if (node is ContainerNode)
                    trackedNodeCollection.Add(node, new TrackedContainerNode(node as ContainerNode, ParentNodeScript, parentResource));
                else if (node is AnimationNode)
                    trackedNodeCollection.Add(node, new TrackedAnimationNode(node as AnimationNode, ParentNodeScript, parentResource));
                else if (node is DefaultNode)
                    trackedNodeCollection.Add(node, new TrackedDefaultNode(node, ParentNodeScript, parentResource));
                else if (node is LoadNode)
                    trackedNodeCollection.Add(node, new TrackedLoadNode(node as LoadNode, ParentNodeScript, parentResource));
                else if (!(node is EntryNode))
                    throw new NotImplementedException("A tracked class has not been created for '" + node.GetType().Name + "'.");
            }

            foreach (Link link in containerNode.LinkCollection)
                trackedLinkCollection.Add(link, new TrackedLink(link, containerNode, ParentNodeScript, parentResource));
        }

        public override void TransferDataReorder()
        {
            base.TransferDataReorder();
            string resourceName = ParentResource.Name;

            CollectionDelta<Link> linkCollectionDelta = new CollectionDelta<Link>(originalLinkCollection, containerNode.LinkCollection);
            CollectionDelta<NodeBase> nodeCollectionDelta = new CollectionDelta<NodeBase>(originalNodeCollection, containerNode.NodeCollection);
            activeTrackedNodes = nodeCollectionDelta.IdenticalItems
                .Where((node) => !(node is EntryNode))
                .Select((node) => trackedNodeCollection[node]);
            activeTrackedLinks = linkCollectionDelta.IdenticalItems.Select((link) => trackedLinkCollection[link]);

            //Remove deleted Links And Nodes
            List<Link> copyOfOriginal = new List<Link>(originalLinkCollection);
            foreach (Link deletedLink in linkCollectionDelta.DeletedItems)
            {
                LinkTransfer.Remove(resourceName, ParentNodeScript.Name, containerNode, copyOfOriginal.IndexOf(deletedLink));
                copyOfOriginal.Remove(deletedLink);
            }

            //RemoveNodes
            foreach (NodeBase deletedNode in nodeCollectionDelta.DeletedItems)
                NodeTransfer.Remove(resourceName, ParentNodeScript.Name, containerNode, deletedNode);

            //Reorder Identical Nodes
            foreach (TrackedNodeBase trackedNode in activeTrackedNodes)
                trackedNode.TransferDataReorder();

            //Add new Links and Nodes
            foreach (NodeBase newNode in nodeCollectionDelta.NewItems)
                NodeTransfer.Add(resourceName, ParentNodeScript.Name, newNode);
             
            foreach (Link newLink in linkCollectionDelta.NewItems)
                LinkTransfer.Add(resourceName, ParentNodeScript.Name, containerNode, newLink);
        }

        public override void TransferDataUpdate()
        {
            base.TransferDataUpdate();

            //Links
            foreach (TrackedLink trackedLink in activeTrackedLinks)
                trackedLink.TransferData();

            //Nodes
            foreach (TrackedNodeBase trackedNode in activeTrackedNodes)
                trackedNode.TransferDataUpdate();
        }
    }
}
